package com.lhq.tag_tales.utils;

import org.springframework.util.Assert;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Map;

public class BeanUtilsCustom {

    private static final Map<Object, Object> processedObjects = new IdentityHashMap<>();

    public static void copyProperties(Object source, Object target) throws IllegalAccessException {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");

        if (source.equals(target)) {
            return;
        }
        if (processedObjects.containsKey(source)) {
            return; // 避免循环引用
        }
        processedObjects.put(source, target);

        // 获取源对象的所有字段
        Field[] sourceFields = source.getClass().getDeclaredFields();
        for (Field sourceField : sourceFields) {
            // 允许访问私有字段
            sourceField.setAccessible(true);
            // 获取源对象的字段值
            Object value = sourceField.get(source);
            if (value != null) {
                try {
                    Field targetField = target.getClass().getDeclaredField(sourceField.getName());
                    targetField.setAccessible(true);

                    // 跳过 static final 字段
                    if (!targetField.isAnnotationPresent(SuppressWarnings.class) &&
                        !Modifier.isStatic(targetField.getModifiers()) &&
                        !Modifier.isFinal(targetField.getModifiers()) &&
                        isTypeCompatible(sourceField.getType(), targetField.getType())) {
                        targetField.set(target, value);
                    }
                } catch (NoSuchFieldException e) {
                    // 记录日志，忽略不存在的字段
                    System.err.println("Field " + sourceField.getName() + " does not exist in target class");
                }
            }
        }

        processedObjects.remove(source);
    }

    private static boolean isTypeCompatible(Class<?> sourceType, Class<?> targetType) {
        return targetType.isAssignableFrom(sourceType) || (sourceType.isPrimitive() && isWrapperType(targetType));
    }

    private static boolean isWrapperType(Class<?> clazz) {
        return clazz.equals(Boolean.class) || clazz.equals(Integer.class) || clazz.equals(Character.class) ||
               clazz.equals(Long.class) || clazz.equals(Double.class) || clazz.equals(Float.class) ||
               clazz.equals(Short.class) || clazz.equals(Byte.class);
    }
}
